#pragma once

enum class ObjectHookEvent
{
	OnSObjUnitEnter,
	OnSObjUnitLeave,
	OnEObjUnitEnter,
	OnEObjUnitLeave,
	OnUnitChangeCombatStatus,
	OnUnitHurted,
	OnUnitKilled,
	OnUnitDead,
	OnPlayerChangeTeam,
	OnPlayerChangeGuild,
	OnPlayerChangeQuestStatus,
	OnPlayerDelete,
	OnSendMessage,
	OnHookDetach,
	Count
};

struct ObjectHookInfo
{
	bool isAvail, isMember;
	std::bitset<std::size_t(ObjectHookEvent::Count)> events;
	LuaRef t;
};

enum class MapHookEvent
{
	OnPlayerPendingEnter,
	OnPlayerLeaveMap,
	OnPlayerEnter,
	OnPlayerLeave,
	OnCreatureSpawn,
	OnStaticObjectSpawn,
	OnUnitHurted,
	OnUnitKilled,
	OnUnitDead,
	OnSendMessage,
	OnHookDetach,
	Count
};

struct MapHookInfo
{
	bool isAvail, isMember;
	std::bitset<std::size_t(MapHookEvent::Count)> events;
	LuaRef t;
};

enum class SpellBuffEvent
{
	OnHurt,
	OnHurted,
	OnKill,
	OnKilled,
	Count
};

struct SpellBuffInfo
{
	bool isAvail, isMember;
	std::bitset<std::size_t(SpellBuffEvent::Count)> events;
	uint64 spellInstGuid;
	const SpellPrototype* pSpellProto;
	uint32 spellLevel;
	uint32 effectIndex;
	uint32 interrupts;
	uint64 duration;
	int64 start;
	LuaRef t;
};

template <std::size_t N>
void HookEvents2bitset(const LuaTable& t, std::bitset<N>& events)
{
	for (lua_Integer i = 1, n = t.len(); i <= n; ++i) {
		events.set(t.get<std::size_t>(i));
	}
}

template <typename HookInfo>
void AddMethod_OnSendMessage(const HookInfo* pHookInfo, LuaTable& t)
{
	auto funcName = pHookInfo->isMember ?
		"AddMethod_OnSendMessage" : "AddMethod_OnSendStaticMessage";
	LuaFunc(t.getL(), funcName).Call<void, LuaTable&>(t);
}

template <typename HookInfo, typename HookEvent, typename... Args>
void ForeachHookInfo4Event(const std::map<uint32, HookInfo*>& hookInfos,
	HookEvent hookEvent, const char* funcName, Args... args)
{
	for (const auto& pair : hookInfos) {
		auto pHookInfo = pair.second;
		if (!pHookInfo->isAvail) {
			continue;
		}
		if (!pHookInfo->events.test(std::size_t(hookEvent))) {
			continue;
		}
		if (!pHookInfo->isMember) {
			LuaFuncs(pHookInfo->t).CallStaticMethod
				<void, Args...>(funcName, std::forward<Args>(args)...);
		} else {
			LuaFuncs(pHookInfo->t).CallMethod
				<void, Args...>(funcName, std::forward<Args>(args)...);
		}
	}
}

template <typename HookInfo, typename HookEvent, typename... Args>
GErrorCode ForeachHookInfo4Test(const std::map<uint32, HookInfo*>& hookInfos,
	HookEvent hookEvent, const char* funcName, Args... args)
{
	for (const auto& pair : hookInfos) {
		auto pHookInfo = pair.second;
		if (!pHookInfo->isAvail) {
			continue;
		}
		if (!pHookInfo->events.test(std::size_t(hookEvent))) {
			continue;
		}
		GErrorCode errCode;
		if (!pHookInfo->isMember) {
			errCode = LuaFuncs(pHookInfo->t).CallStaticMethod
				<GErrorCode, Args...>(funcName, std::forward<Args>(args)...);
		} else {
			errCode = LuaFuncs(pHookInfo->t).CallMethod
				<GErrorCode, Args...>(funcName, std::forward<Args>(args)...);
		}
		if (errCode != CommonSuccess) {
			return errCode;
		}
	}
	return CommonSuccess;
}
